#include "Camera.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/matrix_operation.inl>
#include <memory>
using namespace Umikaze::Core;

std::shared_ptr<Camera> Umikaze::Core::gDefaultCamera =
    std::make_unique<Camera>();

void Camera::useOrthoCamera() { useOrtho = true; }
void Camera::usePerspectiveCamera() { useOrtho = false; }

glm::mat4 Camera::getViewMatrix() const {
  return glm::lookAt(_position, _position + _forward, _up);
}

glm::mat4 Camera::getProjMatrix() const {
  if (useOrtho) {
    return glm::ortho(_leftPlane, _rightPlane, _bottomPlane, _topPlane);
  } else {
    return glm::perspective(_fov, _aspect, _near, _far);
  }
};

glm::vec3 Camera::getPosition() const { return _position; }

void Camera::updateCameraVectors() {
  // calculate the new Front vector
  glm::vec3 front;
  front.x = cos(glm::radians(_yaw)) * cos(glm::radians(_pitch));
  front.y = sin(glm::radians(_pitch));
  front.z = sin(glm::radians(_yaw)) * cos(glm::radians(_pitch));
  _forward = glm::normalize(front);
  // also re-calculate the Right and Up vector
  _right = glm::normalize(glm::cross(
      _forward,
      glm::vec3(0, 1.0f, 0))); // normalize the vectors, because their length
                               // gets closer to 0 the more you look up or down
                               // which results in slower movement.
  _up = glm::normalize(glm::cross(_right, _forward));
}

void Camera::ProcessKeyboard(Camera_Movement direction, float deltaTime) {
  float velocity = 1.0f * deltaTime;
  if (direction == FORWARD)
    _position += _forward * velocity;
  if (direction == BACKWARD)
    _position -= _forward * velocity;
  if (direction == LEFT)
    _position -= _right * velocity;
  if (direction == RIGHT)
    _position += _right * velocity;
  if (direction == UP)
    _position += _up * velocity;
  if (direction == DOWN)
    _position -= _up * velocity;
}

void Camera::ProcessMouseMovement(float xoffset, float yoffset,
                                  bool constrainPitch) {
  xoffset *= 1.0f;
  yoffset *= 1.0f;

  _yaw += xoffset;
  _pitch += yoffset;

  // make sure that when pitch is out of bounds, screen doesn't get flipped
  if (constrainPitch) {
    if (_pitch > 89.0f)
      _pitch = 89.0f;
    if (_pitch < -89.0f)
      _pitch = -89.0f;
  }

  // update Front, Right and Up Vectors using the updated Euler angles
  updateCameraVectors();
}

void Camera::ProcessMouseScroll(float yoffset) {
  _fov -= (float)yoffset;
  if (_fov < 1.0f)
    _fov = 1.0f;
  if (_fov > 45.0f)
    _fov = 45.0f;
}
